#include "lwm2mclient.h"
#include "connection.h"
#include "liblwm2m.h"
#include <internals.h>
#include <sys/select.h>

#define MAX_PACKET_SIZE 2048

#define OBJ_COUNT 3
lwm2m_object_t *objArray[OBJ_COUNT];

typedef struct
{
    lwm2m_object_t *securityObjP;
    lwm2m_object_t *serverObject;
    int sock;
    connection_t *connList;
    int addressFamily;
} client_data_t;

void *lwm2m_connect_server(uint16_t secObjInstID, void *userData)
{
    client_data_t *dataP;
    char *uri;
    char *host;
    char *port;
    connection_t *newConnP = NULL;

    dataP = (client_data_t *)userData;

    uri = get_server_uri(dataP->securityObjP, secObjInstID);

    if (uri == NULL)
        return NULL;

    if (0 == strncmp(uri, "coaps://", strlen("coaps://")))
    {
        host = uri + strlen("coaps://");
    }
    else if (0 == strncmp(uri, "coap://", strlen("coap://")))
    {
        host = uri + strlen("coap://");
    }
    else
    {
        goto exit;
    }
    port = strrchr(host, ':');
    if (port == NULL)
        goto exit;
    // remove brackets
    if (host[0] == '[')
    {
        host++;
        if (*(port - 1) == ']')
        {
            *(port - 1) = 0;
        }
        else
            goto exit;
    }

    *port = 0;
    port++;

    fprintf(stderr, "Opening connection to server at %s:%s\r\n", host, port);
    newConnP = connection_create(dataP->connList, dataP->sock, host, port, dataP->addressFamily);
    if (newConnP == NULL)
    {
        fprintf(stderr, "Connection creation failed.\r\n");
    }
    else
    {
        dataP->connList = newConnP;
    }

exit:
    lwm2m_free(uri);
    return (void *)newConnP;
}

void lwm2m_close_connection(void *sessionH, void *userData)
{
    client_data_t *app_data;
    connection_t *targetP;
    app_data = (client_data_t *)userData;
    targetP = (connection_t *)sessionH;

    if (targetP == app_data->connList)
    {
        app_data->connList = targetP->next;
        lwm2m_free(targetP);
    }
    else
    {
        connection_t *parentP;
        parentP = app_data->connList;
        while (parentP != NULL && parentP->next != targetP)
        {
            parentP = parentP->next;
        }
        if (parentP != NULL)
        {
            parentP->next = targetP->next;
            lwm2m_free(targetP);
        }
    }
}

// // 创建/19/0/0 对象 实例 资源
static void add_object(lwm2m_context_t *lwm2mH)
{
    lwm2m_object_t *objectP;
    objectP = get_test_object();
    lwm2m_add_object(lwm2mH, objectP);
    return;
}

// 从 LwM2M 客户端上下文中发送数据给观察者（watchers）
static void
send_client_data(lwm2m_context_t *contextP)
{
    lwm2m_observed_t *targetP;
    for (targetP = contextP->observedList; targetP != NULL; targetP = targetP->next)
    {
        lwm2m_watcher_t *watcherP;
        uint8_t *buffer = NULL;
        size_t length = 0;
        lwm2m_data_t *dataP = NULL;
        int size = 0;
        coap_packet_t message[1];
        if (LWM2M_URI_IS_SET_RESOURCE(&targetP->uri))
        {
            if (COAP_205_CONTENT != object_readData(contextP, &targetP->uri, &size, &dataP))
                continue;
        }
        for (watcherP = targetP->watcherList; watcherP != NULL; watcherP = watcherP->next)
        {
            if (watcherP->active == true)
            {
                length = lwm2m_data_serialize(&targetP->uri, size, dataP, &(watcherP->format), &buffer);
                coap_init_message(message, COAP_TYPE_NON, COAP_205_CONTENT, 0);
                coap_set_header_content_type(message, watcherP->format);
                coap_set_payload(message, buffer, length);
                watcherP->lastMid = contextP->nextMID++;
                message->mid = watcherP->lastMid;
                coap_set_header_token(message, watcherP->token, watcherP->tokenLen);
                coap_set_header_observe(message, watcherP->counter++);
                (void)message_send(contextP, message, watcherP->server->sessionH);

                printf("Send: ");
                for (ssize_t i = 0; i < length; i++)
                {
                    printf("%02x ", buffer[i]);
                }
                printf("\n");
            }
        }
        if (dataP != NULL)
            lwm2m_data_free(size, dataP);
        if (buffer != NULL)
            lwm2m_free(buffer);
    }
}

// 接收来自网络套接字的数据包，并根据接收到的数据进行处理
static void recvggmsg(client_data_t data, lwm2m_context_t *lwm2mH, fd_set readfds)
{
    uint8_t buffer[MAX_PACKET_SIZE];
    ssize_t numBytes = 0;

    if (FD_ISSET(data.sock, &readfds))
    {
        struct sockaddr_storage addr;
        socklen_t addrLen = sizeof(addr);
        numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen);
        if (0 < numBytes)
        {
            in_port_t port;
            char s[100];
            connection_t *connP;

            struct sockaddr_in *saddr = (struct sockaddr_in *)&addr;
            inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN);
            port = saddr->sin_port;

            // 打印接收到的数据
            // printf("\n----------------------------\n");

            // printf("Received %zd bytes from %s:%d \n", numBytes, s, ntohs(port));
            // printf("ReceData: ");
            // for (ssize_t i = 0; i < numBytes; i++)
            // {
            //     printf("%02x ", buffer[i]);
            // }
            // printf("\n----------------------------\n");

            connP = connection_find(data.connList, &addr, addrLen);
            if (connP != NULL)
            {
                lwm2m_handle_packet(lwm2mH, buffer, (size_t)numBytes, connP);
            }
        }
    }
}

int main()
{
    client_data_t data;
    int result;
    lwm2m_context_t *lwm2mH = NULL;

    /* 绑定登录信息 */
    const char *localPort = "6677";
    const char *server = "119.3.250.80";
    const char *serverPort = LWM2M_STANDARD_PORT_STR;
    char *name = "xxxxxxxxxxx"; // 华为云上面的设备标识码
    int lifetime = 300;         // 保活时间，这个很重要，过了这个时间，就需要重新登录才能发送信息
    char *pskId = NULL;
    uint16_t pskLen = -1;
    char *pskBuffer = NULL;

    /* 创建socket */
    memset(&data, 0, sizeof(client_data_t));
    data.addressFamily = AF_INET;
    data.sock = create_socket(localPort, data.addressFamily);

    /* 获取对象 */
    char serverUri[100];
    int serverId = 123;
    sprintf(serverUri, "coap://%s:%s", server, serverPort);
    objArray[0] = get_security_object(serverId, serverUri, pskId, pskBuffer, pskLen, false);
    data.securityObjP = objArray[0];
    objArray[1] = get_server_object(serverId, "U", lifetime, false);
    objArray[2] = get_object_device();

    /* 协议栈初始化及配置 */
    lwm2mH = lwm2m_init(&data);
    lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray);
    add_object(lwm2mH); // 添加自己的对象

    fd_set readfds;

    struct timeval tv;

    time_t timeout;

    while (1)
    {
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        timeout = 60;
        FD_ZERO(&readfds);
        FD_SET(data.sock, &readfds);

        lwm2m_step(lwm2mH, &timeout);

        printf("tv_sec %ld  lwm2mH->state %d \n", lwm2m_gettime(), lwm2mH->state);

        // 阻塞5s获取服务器发送的数据
        result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
        if (result)
        {
            recvggmsg(data, lwm2mH, readfds);
        }

        // 发送数据
        if (lwm2mH->state == STATE_READY)
        {
            send_client_data(lwm2mH);
        }
    }

    lwm2m_close(lwm2mH);
    close(data.sock);
    connection_free(data.connList);
    clean_security_object(objArray[0]);
    lwm2m_free(objArray[0]);
    clean_server_object(objArray[1]);
    lwm2m_free(objArray[1]);
    free_object_device(objArray[2]);
    return 0;
}
